/**
XenSim simulates xenbal.
Copyright (C) 2006 Travis Vachon

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
**/
package xensim;


public class Domain {
	
	public String name;
	
	public boolean active;
	private SimGlobals globals;
	
	private double bootTime;
	private Double startTime;
	private Double lastTimeCheck;
	private double domainTime;
	
	private double lastWorkUpdateTime;
	
	protected RateFn pagerateFn;
	protected CpuFn workFn;
	protected NetworkFn networkUsageFn;
	
	protected long workToDo;
	protected double timeOnCPU;
	
	public double timeStartedOnNode;
	
	protected boolean migrating;
	
	
	/**
	 * Pages of memory allocated to this domain
	 */
	protected long memoryAllocation;
	protected long memoryPages;
	
	
		
	/**
	 * String representation
	 */
	public String toString(){
		return name + " " +
			   active + " " + 
			   startTime + " " + 
			   pagerateFn + " " +
			   workFn + " " +
			   networkUsageFn + " " +
			   memoryPages;
			   
	}
	
	public Domain(SimGlobals globals,
				  String name,
				  long memoryAllocation,
				  RateFn pagerateFn,
				  CpuFn workFn,
				  NetworkFn networkUsageFn
				  ){
		
		this.name = name;
		
		this.pagerateFn = pagerateFn;
		this.workFn = workFn;
		this.networkUsageFn = networkUsageFn;
		this.globals = globals;
		
		this.domainTime = 0;
		
		this.lastWorkUpdateTime = 0;
		this.workToDo = 0;
		this.timeOnCPU = 0;
		
		
		this.memoryAllocation = memoryAllocation;
		
		this.migrating = false;
		//Start this domain off inactive
		this.active = false;
		
		
	}
	
	public void updateWork(){
		warpedUpdateWork(0);
		
	}
	
	public void warpedUpdateWork(long timeWarp){
		double domainTimeToUse = getDomainTime()+ timeWarp ;
		if(lastWorkUpdateTime < domainTimeToUse){
			long workRequested = workFn.cyclesUsed(lastWorkUpdateTime,domainTimeToUse);
			
			
			
			lastWorkUpdateTime = domainTimeToUse;
			
		
			workToDo += workRequested;
		}
	}

	public double getDomainTime(){
		if(active){
			domainTime += globals.currentTime - lastTimeCheck;
			lastTimeCheck = globals.currentTime;
		}
		return domainTime;
		
	}
	
	/**
	 * Sets boot time. 
	 * 
	 * @throws BootError
	 */
	public void boot(Node bootNode) throws BootError{
		active = false;
		bootTime = globals.currentTime;
		
		this.memoryPages = memoryAllocation/bootNode.pageSize;
		//Make sure we have enough pages of memory reserved for the extra 
		//(so this is a partially full page)
		if (memoryAllocation%bootNode.pageSize > 0) this.memoryPages += 1;
		
	}
	
	/**
	 * Start up domain
	 * @throws StartError 
	 *
	 */
	public void start() throws StartError{
		
		if(!active){
			this.timeStartedOnNode = globals.currentTime;
			active = true;
			startTime = globals.currentTime;
			lastTimeCheck = globals.currentTime;
		}
		else throw new StartError("Domain already active!");
	}
	
	
	/**
	 * Stop domain, add time elapsed to previous time
	 * @throws StopError 
	 *
	 */
	public void stop() throws StopError{
		
		if(active){
			
			active = false;
			
			startTime = null;
			
			domainTime += globals.currentTime - lastTimeCheck;
			
			lastTimeCheck = null;
			
			
			
			//totalWorkDone += workFn.getWorkDone(previousRunTime, domainTime);
		}
		else throw new StopError("Domain not active!");
		
	}
	
	/**
	 * 
	 * @throws ShutdownError
	 */
	public void shutdown() throws ShutdownError{
		if(active){
			try {
				stop();
			} catch (StopError e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	
	}
	
	/**
	 * Returns true if the domain is currently active
	 * @return
	 */
	public boolean isActive(){
		return active;
	}
}